昨天有提到事前規劃,今天就進入開發流程!
創建模型和遷移檔案可以用指令 php artisan make:model Task -m 自動建立
定義欄位規範
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
    use HasFactory;
    protected $fillable = [
        'title',
        'description',
        'completed',
    ];
    protected $casts = [
        'completed' => 'boolean'
    ];
}
在遷移檔案中添加任務的欄位
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up(): void
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('description');
            $table->boolean('completed')->default(false);
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down(): void
    {
        Schema::dropIfExists('tasks');
    }
};
執行遷移來建立資料表
指令 php artisan migrate
controller回顧:第 4 天:控制器
前提摘要
這裡會依照第 24 天:項目重構與優化的方法做分工,主要就是把本來寫在控制器的內容區分成商業邏輯以及拜訪資料庫!
創建 controller 來處理任務的 CRUD 操作
指令 php artisan make:controller TaskController
<?php
namespace App\Http\Controllers;
use App\Http\Requests\TaskRequest;
use App\Models\Task;
use App\Services\TaskService;
use App\Transformers\TaskTransformer;
use \Illuminate\Http\JsonResponse;
class TaskController extends Controller
{
    public function __construct(
        protected readonly TaskService $service,
        protected readonly TaskTransformer $transformer,
    ) {
    }
    /**
     * 取得所有任務明細
     * 
     * @return JsonResponse
     */
    public function showTasks(): JsonResponse
    {
        $tasks = $this->service->showTasks();
        return response()->json($this->transformer->transformTasks($tasks), 200);
    }
    /**
     * 新增任務
     * 
     * @param TaskRequest $request
     * @return mixed JsonResponse
     */
    public function storeTask(TaskRequest $request): JsonResponse
    {
        $validated = $request->validated();
        $task = $this->service->storeTask($validated);
        return response()->json($this->transformer->transformTask($task), 201);
    }
    /**
     * 編輯任務
     * 
     * @param TaskRequest $request
     * @param Task $task
     * @return JsonResponse
     */
    public function updateTask(TaskRequest $request, Task $task): JsonResponse
    {
        $validated = $request->validated();
        $task = $this->service->updateTask($task, $validated);
        return response()->json($this->transformer->transformTask($task), 201);
    }
    /**
     * 改變任務的完成欄位
     * 
     * @param Task $task
     * @param bool $complete
     * @return void
     */
    public function changeTaskComplete(Task $task, bool $complete): void
    {
        $this->service->changeTaskComplete($task, $complete);
    }
    /**
     * 刪除任務
     * 
     * @param Task $task
     * @return void
     */
    public function deleteTask(Task $task): void
    {
        $this->service->deleteTask($task);
    }
}
創建 require validate
指令 php artisan make:request TaskRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
use Symfony\Component\HttpFoundation\Response;
class TaskRequest extends FormRequest
{
    /**
     * 預設function,定義授權規則
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }
    /**
     * 預設 function,定義驗證規則
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [
            'title'         => ['required', 'string', 'max:255'],
            'description'   => ['required', 'string'],
            'completed'     => ['required', 'boolean'],
        ];
    }
    /**
     * 預設 function,可自行定義錯誤訊息
     * 
     * @return string[]
     */
    public function messages()
    {
        return [
            'title.required'         => '標題必填',
            'description.required'   => '內容描述必填',
            'completed.required'     => '完成欄位必填',
        ];
    }
    // 預設 function,驗證失敗時會執行,這邊自定義錯誤時會傳json
    protected function failedValidation(Validator $validator)
    {
        throw new HttpResponseException(
            response: response()->json([
                'status' => 'error',
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], status: Response::HTTP_UNPROCESSABLE_ENTITY)
        );
    }
    // 預設 function,授權失敗時會執行,這邊自定義錯誤時會傳json
    protected function failedAuthorization()
    {
        throw new HttpResponseException(
            response: response()->json([
                'status' => 'error',
                'message' => 'Authorization failed',
            ], status: Response::HTTP_UNAUTHORIZED)
        );
    }
}
創建 transform 轉成與前端約定好的架構
這裡無法下指令,所以直接在 app 下創建一個新的資料夾 Transforms 然後新增一個新的檔案
<?php
namespace App\Transformers;
use App\Models\Task;
use Illuminate\Support\Collection;
/**
 * TaskTransformer
 * 詳細說明: 任務相關資料轉換回傳的處理
 */
class TaskTransformer
{
    /**
     * 把模型的時間轉成 Y-M-D
     * 
     * @param Task $task
     * @return array
     */
    public function transformTask(Task $task): array
    {
        return [
            'id'            => $task->id,
            'title'         => $task->title,
            'description'   => $task->description,
            'completed'     => $task->completed,
            'start_at'      => $task->created_at?->format('Y-m-d'),
            'update_at'     => $task->updated_at?->format('Y-m-d'),
        ];
    }
    /**
     * 把模型集合的時間轉成 Y-M-D
     * 
     * @param Collection $tasks
     * @return array
     */
    public function transformTasks(Collection $tasks): array
    {
        return $tasks->map(fn(Task $task) => [
            'id'             => $task->id,
            'title'          => $task->title,
            'description'    => $task->description,
            'completed'      => $task->completed,
            'created_at'     => $task->created_at->format('Y-m-d'),
            'updated_at'     => $task->updated_at->format('Y-m-d'),
        ])->toArray();
    }
}
service 做邏輯處理這裡無法下指令,所以直接在 app 下創建一個新的資料夾 Services 然後新增一個新的檔案
<?php
namespace App\Services;
use App\Models\Task;
use App\Repositories\TaskRepository;
use Illuminate\Support\Collection as SupportCollection;
/**
 * TaskService
 * 詳細說明: 任務邏輯處理
 */
class TaskService
{
    public function __construct(
        protected readonly TaskRepository $repository,
    ) {
    }
    /**
     * 取得所有任務明細的邏輯處理
     * @return SupportCollection
     */
    public function showTasks(): SupportCollection
    {
        return $this->repository->showTasks();
    }
    /**
     * 新增任務的邏輯處理
     * 
     * @param array $data
     * @return Task
     */
    public function storeTask(array $data): Task
    {
        return $this->repository->storeTask($data);
    }
    /**
     * 編輯任務的邏輯處理
     * 
     * @param Task $task
     * @param array $data
     * @return Task
     */
    public function updateTask(Task $task, array $data): Task
    {
        return $this->repository->updateTask($task, $data);
    }
    /**
     * 改變任務的完成欄位邏輯處理
     * 
     * @param Task $task
     * @param bool $complete
     * @return void
     */
    public function changeTaskComplete(Task $task, bool $complete): void
    {
        $this->repository->changeTaskComplete($task, $complete);
    }
    /**
     * 刪除任務的邏輯處理
     * 
     * @param Task $task
     * @return void
     */
    public function deleteTask(Task $task): void
    {
        $this->repository->deleteTask($task);
    }
}
repository 和資料庫互動這裡無法下指令,所以直接在 app 下創建一個新的資料夾 Repositories 然後新增一個新的檔案
<?php
namespace App\Repositories;
use App\Models\Task;
use \Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection as SupportCollection;
/**
 * TaskRepository
 * 詳細說明: 任務資料邏輯
 */
class TaskRepository
{
    /**
     * 取得所有任務明細的資料處理
     * @return SupportCollection
     */
    public function showTasks(): SupportCollection
    {
        return Task::query()->get();
    }
    /**
     * 新增任務的資料處理
     * 
     * @param array $data
     * @return Task
     */
    public function storeTask(array $data): Task
    {
        return Task::create($data);
    }
    /**
     * 編輯任務的資料處理
     * 
     * @param Task $task
     * @param array $data
     * @return Task
     */
    public function updateTask(Task $task, array $data): Task
    {
        $task->update($data);
        return $task;
    }
    /**
     * 改變任務的完成欄位資料處理
     * 
     * @param Task $task
     * @param bool $complete
     * @return void
     */
    public function changeTaskComplete(Task $task, bool $complete): void
    {
        $task->completed = $complete;
        $task->save();
    }
    /**
     * 刪除任務的資料處理
     * 
     * @param \App\Models\Task $task
     * @return void
     */
    public function deleteTask(Task $task): void
    {
        $task->delete();
    }
}